﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LearnCSharp
{
    /*
        * *************************************************************************************
        * *************************************************************************************
        *                                      CASTING
        * *************************************************************************************
        * *************************************************************************************
    */
    /// <summary> CastingCSharp </summary>
    public class CastingCSharp
    {
        private void UseCasting()
        {
            short s = 2000;
            int i = 10000;
            i = s;  // OK, pas de perte d'info
            Console.WriteLine(" Transtypage i: {0}, {1}", i, i.GetType().ToString());

            float f;
            f = s + i; // OK, pas de perte d'info
            Console.WriteLine(" Transtypage f: {0}, {1}", f, f.GetType().ToString());

            // s = i // KO, car type s trop petit pour recevoir valeur i
            s = (short)i;
            Console.WriteLine(" Transtypage s: {0}, {1}", s, s.GetType().ToString());

            //int i2 = s+f; // KO, car type i perd des info par rapport au type s et f;
            s = 2000;
            float f2 = 1.2F;
            int i2 = (int)(s + f2);
            Console.WriteLine(" Transtypage i2: {0}, {1}", i2, i2.GetType().ToString());

            // int, double ... -> string
            string s1 = "10";
            int i3 = int.Parse(s1);
            Console.WriteLine(" Transtypage i3: {0}, {1}", i3, i3.GetType().ToString());

            // Exception
            string s2 = "666.666";
            int i4 = 0;
            try
            {
                i4 = int.Parse(s2);
                Console.WriteLine(" Transtypage i4: {0}, {1}", i4, i4.GetType().ToString());
            }
            catch (FormatException e)
            {
                Console.WriteLine(" Exception Transtypage : {0}", e);
            }

            Console.ReadLine();
        }
    }

    /*
        * *************************************************************************************
        * *************************************************************************************
        *                                      POLYMORPHYSME
        * *************************************************************************************
        * *************************************************************************************
     * 
     * abstract : déclaration fct abstract avec corp vide (la classe doit être abstraite) + un classe abstraite ne peut instancier un obejt par new + obligé de surcharger la fct 
     * virtual : déclaration fct abstract avec corp rempli (pas de classe virtual) + pas d'obligation de surcharger la fct
     * 
    */

    interface IInterface
    {
    }

    abstract class ClassAbstraite : IInterface
    {
        public static void test()
        {
            ClassBase tb = new ClassBase();
            Console.WriteLine("\n\nClassBase tb = new ClassBase();");
            Console.WriteLine("tb.GetFctAbstract : {0}", tb.GetFctAbstract());
            Console.WriteLine("tb.GetFctVirtual : {0}", tb.GetFctVirtual());
            Console.WriteLine("tb.GetFctSealed : {0}", tb.GetFctSealed());
            Console.WriteLine("tb.GetFctClassique : {0}", tb.GetFctClassique());
            Console.WriteLine("tb.GetFctOuiMaisNon : {0}", tb.GetFctOuiMaisNon());

            ClassBase t = new ClassDeriv();
            Console.WriteLine("\n\nClassBase t = new ClassDeriv();");
            Console.WriteLine("t.GetFctAbstract : {0}", t.GetFctAbstract());
            Console.WriteLine("t.GetFctVirtual : {0}", t.GetFctVirtual());
            Console.WriteLine("t.GetFctSealed : {0}", t.GetFctSealed());
            Console.WriteLine("t.GetFctClassique : {0}", t.GetFctClassique());
            Console.WriteLine("t.GetFctOuiMaisNon : {0}", t.GetFctOuiMaisNon());

            ClassDeriv td = new ClassDeriv();
            Console.WriteLine("\n\nClassDeriv td = new ClassDeriv();");
            Console.WriteLine("td.GetFctAbstract : {0}", td.GetFctAbstract());
            Console.WriteLine("td.GetFctVirtual : {0}", td.GetFctVirtual());
            Console.WriteLine("td.GetFctSealed : {0}", td.GetFctSealed());
            Console.WriteLine("td.GetFctClassique : {0}", td.GetFctClassique());
            Console.WriteLine("td.GetFctOuiMaisNon : {0}", td.GetFctOuiMaisNon());

            Console.WriteLine("\n\nClassAbstraite.  GetFctStatic : {0}", ClassAbstraite.GetFctStatic());
            Console.WriteLine("ClassBase.       GetFctStatic : {0}", ClassBase.GetFctStatic());
            Console.WriteLine("ClassDeriv.      GetFctStatic : {0}", ClassDeriv.GetFctStatic());

            Console.WriteLine("\n\nClassAbstraite.  GetFctStaticSeulementDansClasseAbstraite : {0}", ClassAbstraite.GetFctStaticSeulementDansClasseAbstraite());
            Console.WriteLine("ClassBase.       GetFctStaticSeulementDansClasseAbstraite : {0}", ClassBase.GetFctStaticSeulementDansClasseAbstraite());
            Console.WriteLine("ClassDeriv.      GetFctStaticSeulementDansClasseAbstraite : {0}", ClassDeriv.GetFctStaticSeulementDansClasseAbstraite());

            //Console.WriteLine("ClassAbstraite.  GetFctStaticSeulementDansClasseBase : {0}", ClassAbstraite.GetFctStaticSeulementDansClasseBase());
            Console.WriteLine("\n\nClassBase.       GetFctStaticSeulementDansClasseBase : {0}", ClassBase.GetFctStaticSeulementDansClasseBase());
            Console.WriteLine("ClassDeriv.      GetFctStaticSeulementDansClasseBase : {0}", ClassDeriv.GetFctStaticSeulementDansClasseBase());            

            


            Console.ReadLine();
        }

        

        abstract public string GetFctAbstract();

        virtual public string GetFctSealed()
        {
            return "ClassAbstraite.GetFctSealed";
        }

        public static string GetFctStatic()
        {
            return "ClassAbstraite.GetFctStatic";
        }

        public static string GetFctStaticSeulementDansClasseAbstraite()
        {
            return "ClassAbstraite.GetFctStaticSeulementDansClasseAbstraite";
        }

        

       
    }

    class ClassBase : ClassAbstraite
    {
        public int i,j;

        public ClassBase()
        {
            i = 10;
            j = 20;
        }
        
        override public string GetFctAbstract()
        {
            return "ClassBase.GetFctAbstract";
        }
        
        virtual public string GetFctVirtual()
        {
            return "ClassBase.GetFctVirtual";
        }

        // sealed : arrète le virtual au niveau de la classe en cours, l'appel de la fct ne décendra pas aux classes dérivées
        sealed override public string GetFctSealed()
        {
            return "ClassBase.GetFctSealed";
        }

        public string GetFctClassique()
        {
            return "ClassBase.GetFctClassique";
        }

        new public static string GetFctStatic()
        {
            return "ClassBase.GetFctStatic";
        }

        public static string GetFctStaticSeulementDansClasseBase()
        {
            return "ClassBase.GetFctStaticSeulementDansClasseBase";
        }

        virtual public string GetFctOuiMaisNon()
        {
            return "ClassBase.GetFctOuiMaisNon";
        }
    }

    class ClassDeriv : ClassBase
    {
        override public string GetFctAbstract()
        {
            return "ClassDeriv.GetFctAbstract";
        }

        override public string GetFctVirtual()
        {
            return "ClassDeriv.GetFctVirtual";
        }

        new public string GetFctSealed()
        {
            return "ClassDeriv.GetFctSealed";
        }

        new public string GetFctClassique()
        {
            return "ClassDeriv.GetFctClassique";
        }

        new public static string GetFctStatic()
        {
            return "ClassDeriv.GetFctStatic";
        }

        override public string GetFctOuiMaisNon()
        {
            // base : permet d'appeler la fonction de la classe mère
            return base.GetFctOuiMaisNon();
        }
    }

    class ClassDerivDeDeriv : ClassDeriv
    {
    }

    /*
        * *************************************************************************************
        * *************************************************************************************
        *                                      INTERFACE
        * *************************************************************************************
        * *************************************************************************************
    */

    interface IClasseInterface1
    {
        int a { get; set; }

        void f();
        int fi(int i);
    }

    interface IClasseInterface2
    {
        int a { get; set; }

        void f();
        int fi(int i);
    }

    class A : IClasseInterface1, IClasseInterface2
    {
        int IClasseInterface1.a { get; set; }
        int IClasseInterface2.a { get; set; }

        void IClasseInterface1.f()
        {
        }

        void IClasseInterface2.f()
        {
        }

        int IClasseInterface1.fi(int i)
        {
            return i;
        }

        int IClasseInterface2.fi(int i)
        {
            return i;
        }
    }

    class COVARIANCE_CONTRAVARIANCE
    {
        ClassDeriv f(ClassDeriv cd) { return cd; }
        void fi(IEnumerable<IInterface> li) { }

        delegate void T1(ClassDeriv cd);
        void fpb(ClassBase cd) { }
        void fpd(ClassDeriv cd) { }
        void fpdd(ClassDerivDeDeriv cd) { }

        delegate ClassDeriv T2();
        ClassBase frb() { return new ClassBase(); }
        ClassDeriv frd() { return new ClassDeriv(); }
        ClassDerivDeDeriv frdd() { return new ClassDerivDeDeriv(); }

        delegate void T1bis<in T>(T cd);

        delegate T T2bis<out T>();


        public void Test2()
        {
            //////////////////////////////////////
            // COVARIANCE & CONTRAVARIANCE

            // = EGALITE : COVARIANCE : on peut classe plus petite mais pas plus grande
            ClassBase cb = new ClassBase();
            ClassDeriv cd = new ClassDeriv();
            ClassDerivDeDeriv cdd = new ClassDerivDeDeriv();

            ClassDeriv c;
            // c = cb; // Impossible
            c = (ClassDeriv)cb; // Mais attention au InvalidCastException
            c = cd;
            c = cdd;

            // PARAMETRE FONCTION : COVARIANCE : on peut classe plus petite mais pas plus grande
            //f(cb); // Impossible
            f((ClassDeriv)cb); // Mais attention au InvalidCastException
            f(cd);
            f(cdd);

            // RETOUR FONCTION : CONTRAVARIANCE : on peut classe plus haute mais pas plus petite
            cb = f(c);
            cd = f(c);
            // cdd = f(c); // Impossible 
            cdd = (ClassDerivDeDeriv)f(c); // Mais attention au InvalidCastException

            // TABLEAU de REFRENCE : COVARIANCE
            ClassDeriv[] tab = new ClassDeriv[1];
            //tab[0] = new ClassBase();
            tab[0] = (ClassDeriv)new ClassBase(); // Mais attention au InvalidCastException
            tab[0] = new ClassDeriv();
            tab[0] = new ClassDerivDeDeriv();

            // = EGALITE COLLECTION : INVARIEANCE
            // IDEM PARAMETRE FONCTION COLLECTION
            //<ClassDeriv> lb = new List<ClassBase>();  // Impossible 
            List<ClassDeriv> ld = new List<ClassDeriv>();
            //List<ClassDeriv> ldd = new List<ClassDerivDeDeriv>(); // Impossible 

            // PARAMETRE FONCTION COLLECTION AVEC INTERFACE : COVARIANCE et CONTRAVARIANCE si utilisation de IEnumerable<X> en paramètre de la fonction
            List<ClassBase> lcb = new List<ClassBase>();
            List<ClassDeriv> lcd = new List<ClassDeriv>();
            List<ClassDerivDeDeriv> lcdd = new List<ClassDerivDeDeriv>();

            fi(lcb);
            fi(lcd);
            fi(lcdd);

            // IMPLEMENTATION INTERFACE : INVARIANT
            // f( ClassDeriv cd ) de IIterface
            // ne peute etre implémenté que par f( ClassDeriv cd ) dans les classe dérivée et non par f( ClassBase cd ) ou f( ClassDerivDeDeriv cd )

            // PARAMETRE DELEGUE : CONTRAVARIANCE
            T1 de1 = fpb;
            T1 de2 = fpd;
            //T1 de3 = fpdd; // impossible

            // RETOUR DELEGUE : COVARIANCE
            //T2 de11 = frb; // impossible
            T2 de22 = frd; 
            T2 de33 = frdd;

            // PARAMETRE DELEGUE GENRIQUE : FORCE CONTRAVARIANCE            
            T1bis<ClassDeriv> de222         = fpd;
            T1bis<ClassDerivDeDeriv> de333  = fpd;

            // RETOUR DELEGUE GENRIQUE : FORCE COVARIANCE     
            T2bis<ClassBase> de1111 = frd;
            T2bis<ClassDeriv> de2222 = frd;            
        }
    }
}
